Phase 7.5 — Audio and Video in Pure PHP
Markers: ▶ being worked on,
✓ implemented and waiting to be reviewed,
✓ done,
✓✗ closed
by deciding not to do it, ? open decision,
unmarked = planned.
Arc Post-Mortem Summary
This arc gave Yioop its own audio and video, then put the code that had
grown around them in order. Yioop reads Vorbis, Opus, FLAC and the
container formats in PHP alone, records a message in the browser and plays
it back without ffmpeg, and a page's resource folder recodes what is put
in it (I, II). The library was grouped into folders by what its classes
are for (III), and the four files over five thousand lines were split:
WikiElement gave up its git pages, GroupModel became a base with a wiki
model and a feed model under it, and SocialComponent fell from 16,932
lines to 5,215 as the base that WikiComponent, FeedComponent,
ResourceComponent, MessageComponent, MailComponent and GitComponent
extend (IV).
Two rules came out of that splitting and are now in the process file. A
component never calls another component: what two of them need sits in the
base they extend, is protected rather than private, and takes its
constants with it. And the home of every method is settled before anything
moves, because moving whatever the last measurement named makes new
crossings on each pass. The faults this arc left for Chris to find were
all of one shape, a name that moved out from under a caller, and each is
now a reading a patch cannot pass: every call on a model, every call on a
component, every name a test asks of one.
The arc closed on the screens themselves. The wiki editor's preview
stamps each line of the source it drew, so the line the caret sits on can
be put in the middle of the preview rather than the two panes being lined
up by a ratio of their heights. A wiki link may name the language of the
page it wants, and a resource marker the folder its file sits in, both
read as markup rather than as part of a name.
What this arc is for
Yioop reaches for ffmpeg to make a thumbnail from a video, to work out
how long one runs, and to make over a recorded message. That means a site
cannot do any of it without a program installed beside it, and a site that
has one runs somebody else's program on files that arrived from the web.
Two pure-PHP libraries written elsewhere do both jobs, and this arc brings
them in, wires them where ffmpeg is reached for today, and gives them one
command-line front end (I). It then settles what a page's resource folder
should do about recoding what is dropped into it (II). With the library
folder about to gain a good many files, it groups that folder into
subfolders (III), and closes by looking for the places the code has grown
past what a reader can hold (IV).
- ✓ Bring the pure-PHP audio
and video libraries into
Yioop. Both were written in another conversation. They go
under
src/library/av_processing, in a namespace of
that name. Each gets one command line front end and Yioop's own
unit tests.
- ✓ Split VideoThumb.php
into twenty-three files the autoloader can find. The file
held fifty-eight classes in nineteen thousand lines. They run from
bit readers to whole decoders for five video formats. Every class
another file reaches for now has a file named after it, so nothing
requires anything. Names are built from English words as Coding.pdf
asks, and a docblock says what its subject is for. Checked by
decoding. A ten second MP4 and a five second WebM each give a
thumbnail at the width asked for.
- ✓ Move its command line into
AVTool.php. AVTool.php holds the help text and the option
handler. It makes a picture from a video, and turns a recording into
an MP4 every browser plays.
It refuses to run from a web request. Which half runs is settled by
the file's first bytes, not its name. Twelve cases run it and the
audio class as a person would.
- ✓ Put the audio library under
the same folder and namespace. Thirty-one files came in.
They are readers for Ogg and WebM, a decoder for Opus, an encoder, and
a writer for the sound file. They sit beside
the video work under the arc's namespace, and the loader they
shipped with is dropped. AudioConverter turns a recording into an
MP4 a few seconds at a time, and PlainSound reads WAV and AIFF.
Any kind the folder reads converts to any kind it writes. Reading
Vorbis, MP3 and AAC is not written yet,
and those
are refused with a sentence saying so.
- ✓ Rewrite its tests as Yioop
unit tests. Sixteen test files came in, with five small
recordings and three helper classes. The runner and the test class the
library shipped
with are dropped, since Yioop has both. Each test names the classes by
the arc's namespace and finds its recordings where Yioop keeps them.
They run as they stand:
131 cases pass under
CodeTool.php unit. Two run for about
a hundredth of a second, which is real decoding rather than a fault.
Each says so in its own docblock.
- ✓ A video's thumbnail and
its running time come from the library. Two places asked
ffmpeg for a picture and ffprobe for a running time. Both read
the file with VideoExtractor now. A thumbnail is made where no
ffmpeg is set at all. Measured on a five second WebM: 5.01
seconds, 320 by 240, a still picture in a tenth of a second. Both
thumbnails are WebP now, the moving one named .animated.webp. A served
file's type is read from its name rather than its bytes.
- ✓ Say where ffmpeg is still
reached for. Seven files name it, counting the setting
FFMPEG and the plain word separately. Two are replaced by this
arc, two should be replaced later, and three stay. The audit
below says which is which and why.
- ✓
Replaced by this arc. GroupModel's
convertRecordingToMp4 runs ffmpeg to make a recording over into
mp4; that is item H. UsermessagesElement offers the record button
only where FFMPEG is set, and stops gating on it once H lands.
- ✓
Should be replaced later. VideoConvertJob cuts an
uploaded video into short segments with ffmpeg and stitches them back.
It also recodes avi or mov to mp4. Its
thumbnail step is replaced already. PodcastDownloadJob stitches
podcast segments and now names ffmpeg by the setting. Both are
background jobs run by the media updater.
- ✓
Stays. Config.php declares the setting
itself. PublicHelpPages tells a reader how to install ffmpeg.
GroupModel schedules an uploaded video for recoding behind the
setting. It makes a voice message over with the audio library, which
is item H.
- ✓
Gates removed. Three places asked whether FFMPEG
was set before work that no longer needs it. They are a video page's
size and running time, a video's thumbnail, and VideoConvertJob's
thumbnail step. All three read the file
with the library now.
- ✓ Write unit tests for the
video side. The video library
arrives with none. Each container reader and each decoder gets cases
that read a small file of that kind. They check the frame that comes
back, so one decoder cannot break another unseen.
- ✓ A recorded message is
made over without ffmpeg. Saving a recording turns
whatever arrived into one kind, by handing the file to ffmpeg.
That path uses the audio library instead. A site with no ffmpeg
can then still take a voice message. A browser records the way
built for speech, which Yioop could not decode, so that decoder
is the work below.
- ✓ Read the opening
of a stretch of speech. The marks at the front of a
packet now read, one for each stretch. The kind of sound and the
loudness of each quarter read as well. The tables come from the
counts the standard fixes. Each is walked the way the range
decoder walks a table.
- ✓ Read the shape a
stretch was spoken with. A stretch names a guess from a
fixed book and mends each of its sixteen frequencies. Those
frequencies now read, and are pushed apart where two sit too
close. Every shape of the ten second sample comes back rising in
order, which any mistake in that arithmetic would break.
- ✓ Read the pitch of
a voiced stretch. How far back the sound repeats now
reads, along with a filter of five taps for each quarter. The
distance is carried from one stretch to the next, since a
stretch may write it as a move. Half the jumps between
neighboring stretches are three samples. That is a voice moving
four cycles a second.
- ✓ Read the pulses
of a stretch. The counts, the splitting and the signs
all read now. Against the reference decoder, built here and made
to print what it reads, the two agree sample for sample. That is
198 packets of speech and 63,360 samples. Two faults were found this
way. A block is split one half all the way down before the other, and
the sign sets run seven apart.
- ✓ Turn shape,
pitch and pulses back into samples. The scale each
quarter is played at now comes out of the loudness numbers. All
four scales of all 198 packets match the reference decoder. The
frequencies, the filter terms and the rough sound all match on every
packet. Every packet agrees with the reference decoder, sample for
sample.
- ✓ A stretch says how
much of the earlier sound to lean on. A stretch standing on
its own writes one of three
answers before its pulses. That answer now reads, which is the
first piece of carrying sound between stretches.
- ✓ A voiced stretch
reaches into the sound before it. The samples already played
are run backward through the shape filter. They are then put at the
pitch filter's scale.
The five taps of a pitch filter are held with seven bits of fraction.
The filter works with fourteen, so each is shifted up. The rough sound
of a voiced quarter sits within a percent of the reference. It had no
relation at all before.
- ✓ The whole chain runs
from a file to samples.
SpeechDecoder walks a recording and keeps what one stretch
leaves the next. Ten seconds decodes in a fifth of a second.
AVTool writes the result as a wave file with --speech. Against
the reference decoder it agrees sample for sample: 198 packets
and 63,360 samples.
- ✓ Carry a stretch's
state into the next one. SpeechDecoder keeps the
filters, the loudness and the samples already played across a
packet boundary. A packet may carry a spare copy of a stretch
for a listener who lost the one before. That copy is read past
so the stretch behind it is found, then thrown away.
- ✓ Give the samples to
the converter. AudioConverter sends a speech recording
to the new decoder rather than refusing it. The samples are
raised to forty-eight thousand a second, the rate Opus plays
at, by weighing four samples either side. A browser writes its
recording in parts, and its pieces are found from the heading
of each part. GroupModel makes a voice message over with this
converter, so a site without ffmpeg can take one.
- ✓ Settle what a page's
resource folder does about recoding. A file dropped into
a wiki page's folder is
stored as it arrived, apart from recordings, which are made over.
What should happen for video, and for audio that is already in the
kind Yioop wants, is not settled.
- ✓
Decide what
copyFileToGroupPageResource
should recode. A file carrying pictures is stored as it
arrived. Sound alone is made over into the one kind every browser
plays. The file itself is read rather than its name or the kind
it claims, since a browser recording a voice names itself a video
kind.
- ✓ Read
VideoConvertJob and PodcastDownloadJob against that
decision. VideoConvertJob is deleted. Its only work came
from marker files GroupModel wrote for an uploaded video. That
block is gone, so a video is stored like any other file.
PodcastDownloadJob keeps its own use of the
convert folder and still names ffmpeg by the setting.
- Group src/library into subfolders. The folder holds
files of every kind side by side, and this arc adds a hundred more.
Related files are gathered into subfolders so a reader can find
their way, with the namespaces and every reference following.
- ✓ Say what the groups
are. A pass over the folder
naming which files belong together, so the moving is one decision
rather than a hundred.
- ✓ A refused add user
form comes back holding what was typed. Manage Users
sent the writer to an empty form when the two passwords differed.
The five boxes that are not passwords travel with the refusal, so
the form opens again holding them. A password is not among them,
since it does not belong in an address. The browser compares the
two password boxes before the form is sent, so a mismatch costs no
trip to the server. Where the server does the refusing, the add
user row stays open.
- ✓ Expanding a group's
members leaves the reader looking at them. The members
count on a group's settings page reloads the page with the list
shown. The reload left the reader at the foot of the page rather
than at the list. The Membership fieldset now carries an id and
the link ends with it. The browser lands on that section with its
legend in view.
- ✓ The server says how
much memory it holds against the requests it has served.
A figure that climbs on its own does not say whether the climbing
comes from work done or from time passing. Every five minutes a
line now reports the growth, the requests served between two
lines, and the bytes each. A steady figure per request says
something is held between requests. A falling one says memory is
claimed once and reused.
- ✓ A git object too
large to rebuild is refused rather than fatal.
Rebuilding an object from a delta holds the old and new versions
at once and grows the new one by doubling. That comes to about
two and a half times the object, measured at 218 megabytes for a
90 megabyte rebuild. The ceiling of 512 megabytes therefore
allowed an object the server could not carry, and seekquarry.com
died with a memory fatal. It is now 192 megabytes.
- ✓ Move the files and
follow
the references. Fifty files
moved into six folders. Each gained its group's namespace. A moved class
is named six ways and each was followed. Those are a name written in full,
the L alias, and a name built out of NS_LIB. Also a callback, a __DIR__
load, and a loose library function. A file naming a group takes an alias
for it, such as LW for the wiki group. The root index.php is followed too,
since it sits outside src.
- Look for the places the code has grown past reading.
Yioop has methods and files long enough that a reader cannot hold
them, and passages repeated in several places. This section finds
them and says which are worth taking apart.
- ✓ Find
the methods over five hundred lines. A list of them and what
each does. For each, whether it splits along a line a reader would
know.
- ✓ The edit page form
of the wiki view is split, 1,093 lines to 493.
renderEditPageForm, split by fieldset.
- ✓ The group feed
activity is split, 1,070 lines to 130. groupFeeds, one
method an arm of its switch: toggleThreadMail, addComment,
addFeedContact, addFeedGroup, copyOrCutPost, deletePost,
deleteClip, emptyClip, pasteAllPosts, pastePost, flagPost,
approveFlagged, deleteFlagged, downVotePost, startNewThread,
feedStatus, updatePost, upVotePost.
- ✓ The wiki activity
is split, 930 lines to 189. wiki, into
cleanWikiRequestFields, deleteWikiPage, wikiHistory, wikiPageList,
wikiRelationships, wikiSource, addWikiHistoryPage,
addWikiReadPage, initializeWikiEditAndSource.
- ✓ The wiki editor
activity is split, 906 lines to 198. editWiki, into
editWikiResourceFile, editWikiHeadVars, editWikiPageSettings,
editWikiResourceAction, editWikiUploadResource.
- ✓ The manage groups
activity is split, 820 lines to 323. manageGroups, one
method an arm of its switch: activateOrPromoteUsers,
addUserToGroup, banGroupUser, createGroup, deleteGroup,
deleteGroupUser, groupSettings, infoGroup, changeMailPreference,
inviteGroupUsers, joinGroup, groupGraphStats, setMemberAccess,
setPostLifetime, setVoteAccess, setRegisterType, groupStatistics,
unsubscribeFromGroup.
- ✓ The search sources
activity is split, 646 lines to 263. searchSources, one
method an arm of its switch: addMediaSource, addSubsearch,
clearSourceData, deleteMediaSource, deleteSubsearch,
editSubsearch, editMediaSource, searchMediaSources,
searchSubsearches, testMediaSource.
- ✓ The page options
activity is split, 627 lines to 277. pageOptions, into
pageOptionsCrawlSettings and pageOptionsTestPage. This one has no
switch: it is a run of blocks, and the two taken out are the
settling of what a crawl will run with and the trying of one page
through the processors.
- ✓ The manage users
activity is split, 565 lines to 172. manageUsers, one
method an arm of its switch: addUser, editUser, actAsUser,
deleteUser, addUserRole, addUserGroup, deleteUserRole,
deleteUserGroup, searchUsers, updateUserStatus.
- Put back the methods that were never worth making, and take
out the passages that are. A method under five lines called
fewer than three times, and a method under ten lines called fewer than
twice, buys a reader nothing: its body goes back where it was called
from. A passage over five lines standing in three places or more is the
opposite case, and wants a method of its own. Each with what it does and
where it should live.
- ✓ Forty-one methods
nothing can reach are taken away. Together they held
1,043 lines. devlog/check_orphans.sh names a method only
where its name appears nowhere else in the source at all, since a
name is reached in ways one line does not show: IndexShard's
makeWords is written inside a longer string and called through
it, and MailSite declares userExists and getPasswordHash for the
class under library/mail to answer for. Both were taken away by a
looser rule first and stopped the test run.
- ✓ The classifier
tool's seven methods are put back. They were taken away
as unreachable, but its main builds the name of what to run out
of what the command line said, so run{$activity} reaches
runTrainAndTest and that reaches the other six. The check now
reads the fronts and backs joined into a name that is called
through a variable, so ArcTool's outputInfo and this run are both
known. An advertisement comparator is put back too, and the check
leaves what handles advertisements and credits alone, since a
script people buy calls into it from outside this source.
- ✓ Methods nothing
can reach are taken away. Sixty-two of them, near five
thousand lines, from a tokenizer that ran another language’s
interpreter to the deprecated index shard’s slice readers.
A method only a test called went with its case, since such a case
stands for a caller that is not there.
- ✓ A short method
called once or twice goes back where it was called.
Ninety of them. The name bought a reader nothing: it had to be
looked up to learn what the one line did. Each was moved by hand
with the file read back after it, and where a test named one, the
case now holds what the method held.
- ✓ What may move is
decided by what the body reaches. A body reaching
anything private or protected stays where it is, since the caller
may not reach it; so does one naming its own class, one whose
file the caller does not import from, one that writes a file or
reaches the database, a view drawing a screen, and an empty hook
a subclass fills.
- ✓ The reading that
decides what can be reached is made to match how Yioop calls
things. A name built at run time, a name inside a
string, a name spelt with different case, a name only a comment
holds, and the root index.php all count now. Each was added after
a method it had missed was taken away and something broke: the
media updater, the server’s timer, the classifier tool.
- ✓ The dev scripts
move into devlog/devscripts. check_git_clone.sh and
drive_screens.js are gone with the browser scripts that had
replaced the second, and every path inside the remaining scripts
and in the process file points at the new folder.
- ✓ The wiki editor's
page settings are read again. Splitting editWiki lifted
the reading of them into editWikiHeadVars and left nothing
calling it, so every setting a writer picked was dropped on save.
editWikiPageSettings calls it now. Two cases pin what it reads,
and check_orphans.sh is what found it.
- Find the files over five thousand lines. Eight
are that long. Five are set aside: the three atto servers are
another project's copy, PublicHelpPages.php is generated, and
H264Decoder.php holds fifteen classes that belong together. The
three that remain are Yioop's own, and they are read below by
what each method is about and by how often one subject's methods
call another's.
- ✓ The git pages
move out of the wiki view. Twenty-three methods and
1,178 lines drawing a repository, its readme, its history, its
statistics and its issues become GitElement, and WikiElement
falls from 5,770 lines to 4,594. The wiki view reaches the new
element by name through the view helper at the four places it
drew a git page itself.
- ✓ The group model
becomes a base with two children. GroupModel held
groups, wiki pages and the feed in 6,793 lines. It now holds
groups and their members in 2,623 lines. WikiModel holds the
pages, their settings, their resources and the git issues.
FeedModel holds the threads and posts. Both extend GroupModel,
neither extends the other, and the base calls neither. The two
page lookups both children need stayed in the base. Callers ask
the model that holds what they want, which took 298 call sites
across twenty files.
- ✓ Mail moves out of
the social component. The 55 methods that read and
write a mail account, its mailboxes and its messages become
MailComponent, 3,723 lines, and SocialComponent falls from
16,932 lines to 13,358. Four calls cross the seam and each goes
through the controller. The userMail activity moves to the new
component in AdminController and GroupController, or the
activity is not found at run time. Twenty-three tests on the
request argument become three lists and one built method
name.
- ✓ The wiki moves
out of the social component. Its 53 wiki methods and the
21 git issue methods, about 6,250 lines with their support,
become WikiComponent, and what stays is the feed, groups,
contacts, ballots and bots at about 6,000. The activity wiki
moves to the new component in AdminController, GroupController
and ApiController. As built, five components came out of
SocialComponent rather than one: the wiki, the group feed, the
files kept with a page, the messages between two users and the
git pages, and the base fell to 5,215 lines. The home of every
method is settled first from what calls it, since moving
whatever the last measurement named makes new crossings each
pass. A method two components need sits in the base and is
protected rather than private, and the constants a moved method
reads move with it.
- Other work accomplished, oldest first. Each step below
was reached while working on something above. It is kept here so the arc says
what happened.
- ✓ A paste of resources
lands in the language being looked at. A page has a copy
in each language, each with resources of its own. The links out of
the resources page dropped the language. A paste made on the
Vietnamese page went to the English one. Every
link from that page now carries it.
- ✓ A public page
shows its group's header and footer. A group's owner sets a
header page, a footer page and a theme once for the group. The
group side of the site filled these into a page's head. The static
side did not, so a p address showed neither. It fills them the
same way now. A page names its own only where the group allows
it.
- ✓ A locale named in
an address reaches the page lookup. Yioop's own web
server answers many requests in one process. The locale was held
in a static that outlived a request. The first request settled the
locale for every request after it, so l in a later address was
ignored. RequestScope keeps a value in $_SERVER, which that server
saves and puts back around every handover. A value there acts like
a static but belongs to one request. The locale is kept there, and
so is the list of names this machine answers to.
- ✓ Either part of a
choosing list scrolls on its own. The chosen items and
the rest now sit in a list of their own inside the outer one.
A part holding more than seven items scrolls by itself. A long
list of chosen items no longer pushes the rest off the screen.
With nine items each, both parts stand 384 pixels tall over 642
pixels of items. With two and three, neither scrolls.
- ✓ A double click
carries an item across the choosing line. A wiki form's
choosek list has a line dividing what is chosen from the rest.
Dragging across it is awkward on a long list. A double click below
the line sends an item to the top of the chosen. One above it goes
to the top of the rest.
- ✓ Read the sound a
media file carries, in PHP alone. Yioop takes the sound out
of a video and reads plain sound files. It decodes the kinds a browser
sends. Each part below is measured against
ffmpeg's decoding of the same file.
- ✓ Take the sound
track out of a video file. SoundTrack finds the sound of
an MP4, WebM or Ogg file without decoding a picture. Naming a
video and a sound file writes that sound on its own. Five seconds
of a WebM gave 240,960 samples as a WAV.
- ✓ Read and write the
sound files that keep their samples as they are.
PlainSound reads and writes WAV and AIFF. A file's kind is settled
by its first bytes rather than its name. Sound passes through a
couple of seconds at a time. A four minute song converts with no
large file beside it.
- ✓ Decode AAC, the
sound an MP4 usually carries. AacDecoder reads a frame
part by part and all eleven tables of codes. Every one of
ffmpeg's ninety-five frames for a tone decodes. A stereo song
differs from ffmpeg by six thousandths a sample.
- ✓ Decode Vorbis, the
sound an Ogg file usually carries. VorbisSetup reads the
tables and VorbisDecoder turns packets into samples. Twenty
seconds of a song differs from ffmpeg by one ten-thousandth a
sample, with no shifting. The codes a book uses are worked out
the way this format asks, which is not the usual way.
- ✗
Decode MP3. MP3 needs machinery of its own. That
is a bank of filters, the side information beside each frame, and
their tables. None of it is shared with the work
already here.
- ✗ Write Vorbis and MP3,
not only read them. An
encoder for each would let a caller ask for either kind. Yioop
needs neither today, so both wait.
- ✓ Mend how a media
list draws its rows. Rows were drawn one over another
and the count on a folder was painted over by it. The head of the
list is closed whether or not a heading row is written. A cell is
as tall as its row, and the count has a place of its own.
Measured on a media list raised in the sandbox: rows at 98, 166,
234 and 302.
- ✓ Draw a document's
cover the way the page itself draws it. A thumbnail is
drawn by walking the page's own drawing. Four magazine covers
went from 98, 15, 95 and 33 to 13.7, 13.2, 12.4 and 6.7 of 255.
That measure is against another reader's rendering of the same
page.
- ✓ A blob read asks
only for what the file holds. The live server died
asking for a string of 278 megabytes while near its limit. A
read takes its length from an index and asks for all of it
before reading. A wrong length asks for that much whatever the
file holds. The length is cut to what is left in the file. One
past 32 megabytes is refused, with a log line naming the file
and offset.
- ✓ A stored video keeps
its pictures. Every file put in a page's resource folder
was offered to the voice message converter. It decided by the
file's ending. A downloaded show named .mp4 was taken for a
recording and written over with its sound alone. The converter
now reads the file: one carrying pictures is left as it
arrived.
- ✓ A podcast item is
fetched with ffmpeg where it names the pieces of a
video. downloadPodcastItem read such a playlist itself,
chose a quality, fetched the pieces and joined them. ffmpeg does
all of that from the playlist's address alone, so 173 lines came
out for 124. A file that is not a playlist is fetched as a page
with no ffmpeg run on it. The pieces are copied as they
stand and nothing is written again. ffmpeg's progress reaches the
log or the terminal every five seconds. Making a thumbnail for
such a video asked a smoothing table for a column it does not
hold, which stopped the media updater.
- ✓ A picture file gets
its own thumbnail drawn. A thumbnail is kept under the
file's whole name with .webp added. The one for a_picture.webp is
a_picture.webp.webp. The controller left the ending off where a
name already carried it. That asked for the file itself and drew
nothing. It adds the ending wherever such a file stands
beside it, and leaves the bare name only where none does.
- ✓ Choosing a language
keeps the screen a writer is on. The language dropdown
built an address ending arg=edit and nothing else. A writer
looking at a page's resources landed on its text opened for
editing. It keeps what is being looked at now, read from the
same two names the rest of the view reads. A redirect keeps the
language too, so a paste comes back on the copy it was made
on.
- ✓ The emoji picker
drops the descriptions that overrode a screen reader.
Each emoji carried a short name a writer types and a description,
and the description became a label. A reader's own software names
an emoji in the reader's own language, so the label replaced that
with one language for everybody. The 1,823 descriptions are gone
from 25 locales. The array moved out of the constructor into a
method of its own.
- ✓ Two long view
methods are split by fieldset. ServersElement::render
drew six fieldsets in one method of 1,096 lines, and
ManagegroupsElement::renderGroupsForm drew five in 601. Each
fieldset is now a method named for the help button already in it.
A fieldset works out for itself what it needs about the form,
since a value the old method held is not there once the fieldset
moves out.
- ✓ The wiki editor is
split into four methods. renderEditPageForm was 1,093
lines. It is now 493, with renderEditSettings, renderEditBody and
renderEditButtons beside it. The thirteen facts its markup reads
throughout became properties first, since a stretch cannot move
while those are locals. Five names ending in a kind were renamed
with them. The whole page, compared byte for byte on all three
screens, differs only where the token differs.
- ✓ The group feed's
switch becomes eighteen methods. groupFeeds ran to 1,070
lines because every thing a reader can do to a feed sat in one
switch. Each arm is now a method named for what it does, and the
switch chooses between them. Two labels sharing a body share a
method, and a break inside a moved arm became a return.
- ✓ A VP8 macroblock
predicted in sub-blocks no longer reads past its residuals.
reconstructB overwrote its two loop counters with sample positions
inside the loop that used them. After the first sub-block the
residual was looked for at a key past the sixteen a macroblock
holds, and every frame wrote warnings. The sample positions are
named apart from the counters.
- ✓ A VP8 macroblock
predicted in sub-blocks no longer reads past its
residuals. reconstructB overwrote its two loop counters
with sample positions inside the loop that used them. After the
first sub-block the residual was looked for at a key past the
sixteen a macroblock holds, and every such frame wrote warnings.
The sample positions are named apart from the counters.
- ✓ A search asks for
no more results than a reader would. The limit a request
sends decides how many results are gathered. It also decides how
many summaries are loaded after them. It was unbounded, so one
request could ask for as much memory as it liked. It is held at a
thousand now. A posting list claiming more items than its bytes can
hold is refused.
- ✓ The eighteen
feed methods are given the values their bodies read.
Splitting groupFeeds left each moved body naming locals the old
method had worked out above the switch. Sixteen of the eighteen
named a group model and none set it, so emptying the clipboard
died on a call to a method on null. Each method works out what it
reads now, and two small methods give the request's numbers and a
post's title and description.
- ✓ A cut post is
taken away again after being copied. Before the split
the cutpost label fell through into deletepost, and the split
turned that into a break, so a cut copied and left the post
standing. The switch falls through again. Pasting every clipped
post also copied resources from an item that was never named,
where it meant the item the loop is on.
- ✓ The wiki activity
is split into eleven methods. wiki ran to 930 lines,
holding a switch of twelve labels and 318 lines of drawing after
it. It is 189 lines now. The four long arms, the field cleaning,
and the three parts of the drawing each became a method. The loop
that made locals out of field names is gone, so a reader and a
checker can both see what each part reads. What the method hands
the view on twelve screens is unchanged, byte for byte.
- ✓ A checkmark no
longer stops a patch being cut. The cut refused a plan
whose lines were rewritten, which is what setting a checkmark
does. A removed line whose added counterpart differs only in its
marker spans now counts as a marker change rather than a removal.
A line that leaves the plan outright still stops the cut.
- ✓ The wiki editor's
activity is split into six methods. editWiki ran to 906
lines. It is 198 now, with the file kept beside a page, the page's
own settings, the values at the head of a page, the actions on the
files beside it, and taking a file up, each a method. What the
activity hands the view on seventeen screens is unchanged, byte
for byte, the five new ones covering the settings screen, a page
that is not there yet, a file opened for writing, a writing mark
put back, and the files listing open.
- ✓ The parts of the
wiki and feed activities that stand on their own are checked by
unit tests. Six cases read the request fields the two
activities work from: a number that is absent, a zero, a title
and a description cut to the length a post may hold, a page name
written with a space, the two fields the wiki cannot do without,
a group named by its name, and a page filled in from a template.
Three methods were made public so a case can reach them. The
whole suite runs 1,096 cases and 43,671 assertions.
- ✓ Naming a group by
its name clears the report that the group is absent. A
request naming a group by name and not by number was refused for
missing fields, since the report was cleared under the name of
the field being read rather than under the name of the group. A
unit test found this. It is cleared under group_id now, which is
the name the report is written under.
- ✓ Fifteen screens
are driven in a browser and match what they drew before.
Yioop was stood up and a headless Chrome signed in and walked the
wiki and the feed: reading, the page list plain and filtered, the
source, four editing screens, the history, the folder, the
related pages, and the feed. Each was walked against the
confirmed tree and against this one, and what the browser read
off the pages is the same.
- ✓ CodeTool gains a
probe that says what an activity hands its view. A long
method is split by moving parts of it out, and the question that
answers is whether any screen it serves now shows something
different. probe calls one activity once per screen named in a
file and prints the whole of what it hands the view. The reader's
mark, a hash and a count of seconds are written over, so two runs
of an unchanged activity match to the byte.
- ✓ The audit runs as
one command. The eleven checks before a patch were run
one at a time over eight scripts and the table was written by
hand, so a table could say a check passed that was never run.
devlog/audit.sh runs them over the files the patch carries and
prints the table itself. It found a sandbox Config.php committed
into this very patch.
- ✓ A sandbox says
what it is missing. devlog/check_env.sh names what a
work clone needs and what is absent: PHP and its extensions, the
word list the docblock check reads, a configured work directory,
the two databases, and a browser to drive. The word list being
absent had turned one real docblock fault into ninety-one, which
is the kind of absence that hides rather than stops.
- ✓ An old file is
brought up one patch at a time. A patch touching a file
with two hundred docblocks that say nothing cannot rewrite them
all and do its own work. The cut now refuses only the first such
file the patch touches and names the rest as waiting. The test
file for the group and wiki component is this patch's, along with
the tool file it touches.
- ✓ A browser walks
the same screens the probe reads. devlog/drive_screens.js
signs in to a standing Yioop and fetches each screen a file names,
reading what the browser puts on the page. Each screen is fetched
in a context of its own, so a message one leaves cannot show on
the next, which is the fault that made an earlier comparison
disagree with itself. How long ago a row changed is written over,
so two runs a minute apart match to the byte.
- ✓ A log message
that repeats is counted rather than written every time.
Reading a broken index wrote tens of thousands of entries a
second until the log was the slow part of the machine.
crawlLogCounter stands in for crawlLog and writes one summary
every so many seconds. It holds the count in memory and touches
no file. The four posting decode guards call it, and their lines
of figures are gone, since each figure would be counted as a
message of its own.